package com.employ.admin.utils;

import javax.crypto.Cipher;
import java.io.*;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;

/**
 * import java.io.FileInputStream;
 * import java.io.FileNotFoundException;
 * import java.io.FileOutputStream;
 * import java.io.IOException;
 * import java.io.ObjectInputStream;
 * import java.io.ObjectOutputStream;
 * import java.security.Key;
 * import java.security.KeyPair;
 * import java.security.KeyPairGenerator;
 * import java.security.NoSuchAlgorithmException;
 * import java.security.interfaces.RSAPrivateKey;
 * import java.security.interfaces.RSAPublicKey;
 *
 * import javax.crypto.Cipher;
 * import javax.crypto.NoSuchPaddingException;
 */

/**
 * 缺点编辑
 * 1）产生密钥很麻烦，受到素数产生技术的限制，因而难以做到一次一密。
 * 2）安全性，RSA的安全性依赖于大数的因子分解，但并没有从理论上证明破译RSA的难度与大数分解难度等价，而且密码学界多数人士倾向于因子分解不是NP问题。现今，人们已能分解140多个十进制位的大素数，这就要求使用更长的密钥，速度更慢；另外，人们正在积极寻找攻击RSA的方法，如选择密文攻击，一般攻击者是将某一信息作一下伪装（Blind），让拥有私钥的实体签署。然后，经过计算就可得到它所想要的信息。实际上，攻击利用的都是同一个弱点，即存在这样一个事实：乘幂保留了输入的乘法结构：
 * （XM)d = Xd *Md mod n
 * 前面已经提到，这个固有的问题来自于公钥密码系统的最有用的特征--每个人都能使用公钥。但从算法上无法解决这一问题，主要措施有两条：一条是采用好的公钥协议，保证工作过程中实体不对其他实体任意产生的信息解密，不对自己一无所知的信息签名；另一条是决不对陌生人送来的随机文档签名，签名时首先使用One-Way Hash Function对文档作HASH处理，或同时使用不同的签名算法。除了利用公共模数，人们还尝试一些利用解密指数或φ（n）等等攻击.
 * 3）速度太慢，由于RSA 的分组长度太大，为保证安全性，n 至少也要 600 bits以上，使运算代价很高，尤其是速度较慢，较对称密码算法慢几个数量级；且随着大数分解技术的发展，这个长度还在增加，不利于数据格式的标准化。SET(Secure Electronic Transaction）协议中要求CA采用2048比特长的密钥，其他实体使用1024比特的密钥。为了速度问题，人们广泛使用单， 公钥密码结合使用的方法，优缺点互补：单钥密码加密速度快，人们用它来加密较长的文件，然后用RSA来给文件密钥加密，极好的解决了单钥密码的密钥分发问题。
 */
public class RSATool {
    public static void makekeyfile(String pubkeyfile, String privatekeyfile)
            throws NoSuchAlgorithmException, FileNotFoundException, IOException {
        // KeyPairGenerator类用于生成公钥和私钥对，基于RSA算法生成对象
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        // 初始化密钥对生成器，密钥大小为1024位
        keyPairGen.initialize(1024);
        // 生成一个密钥对，保存在keyPair中
        KeyPair keyPair = keyPairGen.generateKeyPair();

        // 得到私钥
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

        // 得到公钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();

        // 生成私钥
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
                privatekeyfile));
        oos.writeObject(privateKey);
        oos.flush();
        oos.close();

        oos = new ObjectOutputStream(new FileOutputStream(pubkeyfile));
        oos.writeObject(publicKey);
        oos.flush();
        oos.close();

        System.out.println("make file ok!");
    }

    /**
     *
     * @param k
     * @param data
     * @param encrypt
     *            1 加密 0解密
     * @throws Exception
     */
    public static byte[] handleData(RSAPrivateKey k, byte[] data, int encrypt)
            throws Exception {

        if (k != null) {

            Cipher cipher = Cipher.getInstance("RSA");

            if (encrypt == 1) {
                cipher.init(Cipher.ENCRYPT_MODE, k);
                byte[] resultBytes = cipher.doFinal(data);
                return resultBytes;
            } else if (encrypt == 0) {
                cipher.init(Cipher.DECRYPT_MODE, k);
                byte[] resultBytes = cipher.doFinal(data);
                return resultBytes;
            } else {
                System.out.println("参数必须为: 1 加密 0解密");
            }
        }
        return null;
    }

    public static void main(String[] args) throws Exception {

        String pubfile = "d:/temp/pub.key";
        String prifile = "d:/temp/pri.key";

        makekeyfile(pubfile, prifile);

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(pubfile));
        RSAPublicKey pubkey = (RSAPublicKey) ois.readObject();
        ois.close();

        ois = new ObjectInputStream(new FileInputStream(prifile));
        RSAPrivateKey prikey = (RSAPrivateKey) ois.readObject();
        ois.close();

        // 使用公钥加密
        String msg = "~O(∩_∩)O哈哈~";
        String enc = "UTF-8";

        // 使用公钥加密私钥解密
        System.out.println("原文: " + msg);
        byte[] result = handleData((RSAPrivateKey) pubkey, msg.getBytes(enc), 1);
        System.out.println("加密: " + new String(result, enc));
        byte[] deresult = handleData(prikey, result, 0);
        System.out.println("解密: " + new String(deresult, enc));

        msg = "嚯嚯";
        // 使用私钥加密公钥解密
        System.out.println("原文: " + msg);
        byte[] result2 = handleData(prikey, msg.getBytes(enc), 1);
        System.out.println("加密: " + new String(result2, enc));
        byte[] deresult2 = handleData((RSAPrivateKey) pubkey, result2, 0);
        System.out.println("解密: " + new String(deresult2, enc));

    }

}
